using UnityEngine;
using System;
using System.Reflection;
using Fusion.Utilities.Singleton;
#if FUSION_DYNAMIC_DLL
using ILRuntime.CLR.TypeSystem;
using Fusion.Frameworks.Assets;
using System.IO;
#endif

namespace Fusion.Hotfix
{
    [DisallowMultipleComponent]
    public class DLLManager : SingletonBehaviour<DLLManager>
    {
        private static ILRuntime.Runtime.Enviorment.AppDomain appDomain = null;
        public static ILRuntime.Runtime.Enviorment.AppDomain AppDomain { get => appDomain; }

        public T Instantiate<T>(string className, params object[] objects)
        {
#if FUSION_DYNAMIC_DLL
            return appDomain.Instantiate<T>(className, objects);
#else
            Type classType = GetType(className);
            return classType != null ? (T)Activator.CreateInstance(classType, objects) : default(T);
#endif
        }

        public object InvokeStatic(string className, string method, params object[] objects)
        {
#if FUSION_DYNAMIC_DLL
            return appDomain.Invoke(className, method, null, objects);
#else
            Type classType = GetType(className);
            MethodInfo methodInfo = classType.GetMethod(method);
            return methodInfo.Invoke(null, objects);
#endif
        }

        public object Invoke(string className, object instance, string method, params object[] objects)
        {
#if FUSION_DYNAMIC_DLL
            return appDomain.Invoke(className, method, instance, objects);
#else
            Type classType = GetType(className);
            MethodInfo methodInfo = classType.GetMethod(method);
            return methodInfo.Invoke(instance, objects);
#endif
        }

        public void SetValue<T>(string className, object obj, string field, T value, BindingFlags bindingFlags = BindingFlags.Public)
        {
            Type classType = GetType(className);
            FieldInfo fi = classType.GetField(field, bindingFlags | BindingFlags.Instance);
            fi.SetValue(obj, value);
        }

        public MethodInfo GetMethod(string className, string methodName, Type[] types, BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
        {
            Type classType = GetType(className);
            if (types != null)
            {
                return classType.GetMethod(methodName, bindingFlags, Type.DefaultBinder, types, null);
            }
            else
            {
                return classType.GetMethod(methodName, bindingFlags);
            }
        }

        public Type GetType(string className)
        {
#if FUSION_DYNAMIC_DLL
            IType it = appDomain.LoadedTypes[className];
            return it?.ReflectionType;
#else
            Type type = Type.GetType($"{className}, Assembly-CSharp");
            return type;
#endif

        }

        public override void Initializate()
        {
            base.Initializate();
#if FUSION_DYNAMIC_DLL
            appDomain = new ILRuntime.Runtime.Enviorment.AppDomain();
#if DEBUG && !NO_PROFILER
            appDomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
#endif

            TextAsset textAsset = AssetsManager.Instance.Load<TextAsset>("Scripts/Extra");
            MemoryStream memoryStream = new MemoryStream(textAsset.bytes);

            appDomain.LoadAssembly(memoryStream);
            LitJson.DynamicDLL.JsonMapper.RegisterILRuntimeCLRRedirection(appDomain);
            Type classType = Type.GetType("Fusion.Hotfix.DLLCustomBinder, Assembly-CSharp");
            DLLBinder dllBinder = classType != null ? (DLLBinder)Activator.CreateInstance(classType, appDomain) : new DLLBinder(appDomain);
            dllBinder.Register();
#endif
        }
    }
}

